/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package gestor.base.de.datos;

import java.io.*;
import java.text.*;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author Ana Silvia Gonzáles
 * @author Diego Enríquez
 */
public  class GestorBaseDeDatos {

    
    private String dir;
    private Map<String, DataBase> dataBases;
    private DataBase current;
    
    public GestorBaseDeDatos()
    {
        dataBases=new HashMap<>();
        current=null;
        
        dir="../DataGestor";
        File saveDir=new File("../DataGestor");
        if(!saveDir.exists())
        {
            if(saveDir.mkdirs()) {
            }
            else {
                gui.GUI.error("No pudo crearse el directorio para las bases de datos");
            }
        }
        else
        {
            loadNames();
            
        }
        
        System.out.println(saveDir.getPath());
        
    }

    public void setDir(String dir) {
        this.dir = dir;
    }

    public void setDataBases(Map<String, DataBase> dataBases) {
        this.dataBases = dataBases;
    }

    public boolean setCurrent(String nombre){
        try {
            return current(nombre.toUpperCase());
        } catch (IOException | ClassNotFoundException ex) {
            Logger.getLogger(GestorBaseDeDatos.class.getName()).log(Level.SEVERE, null, ex);
            return false;
        }
    }
    
    public boolean current(String current) throws FileNotFoundException, IOException, ClassNotFoundException {
        boolean resp=true;
        String ant = null;
        
        if(this.current!=null) {
            ant=this.current.getName();
        }
        
        if(dataBases.containsKey(current.toUpperCase())) {
            if(loadDataBase(current.toUpperCase())) {
                this.current = dataBases.get(current.toUpperCase());
                gui.GUI.error("\nLA BASE DE DATOS ACTUAL ES "+this.current.getName());
            }
            else {
                resp=false;
            }
        }
        else {
            resp=false;
        }
        
        if(ant!=null) {
            saveDataBase(ant);
        }
            
        
        return resp;
    }

    public String getDir() {
        return dir;
    }

    public Map<String, DataBase> getDataBases() {
        return dataBases;
    }

    public DataBase getCurrent() {
        return current;
    }
    
    /**
     * Carga todos los nombre de la base de datos
     */
    private void loadNames()
    {
     
        File saveDir=new File(dir);
        File listDir[]=saveDir.listFiles();
        
            for(int i=0;i<listDir.length;i++) {
            if(listDir[i].isDirectory())
            {
                DataBase nueva=new DataBase(listDir[i].getName());
                System.out.print(listDir[i].getName());
                dataBases.put(nueva.getName(), nueva);
                
            }
        }
    }
    
    /**
     * Carga una base de datos
     * @param nombre
     * @return
     * @throws FileNotFoundException
     * @throws IOException
     * @throws ClassNotFoundException 
     */
    public boolean loadDataBase(String nombre) throws FileNotFoundException, IOException, ClassNotFoundException
    {
        
        boolean resp=true;
        
        if(dataBases.containsKey(nombre.toUpperCase()))
        {
            if(!dataBases.get(nombre.toUpperCase()).isIni())
            {
                File db=new File(dir+"/"+nombre.toUpperCase()+"/"+nombre.toUpperCase()+".popo");
                if(db.exists())
                {     
                    try (FileInputStream fileIn = new FileInputStream(db.getPath());
                            ObjectInputStream in = new ObjectInputStream(fileIn))
                            {
                            dataBases.put(nombre.toUpperCase(), (DataBase)in.readObject());
                            dataBases.get(nombre.toUpperCase()).setIni(true);
                            }
                }
                else {
                    resp=false;
                }
            }
        }
        else {
            resp=false;
        }
        
        return resp;
        
    }
    
    /**
     * Guarda una base de datos
     * @param nombre
     * @return 
     */
    public boolean saveDataBase(String nombre){
        try {
            return save(nombre.toUpperCase());
        } catch (Exception ex) {
            Logger.getLogger(GestorBaseDeDatos.class.getName()).log(Level.SEVERE, null, ex);
            return false;
        }
    }
    
    public boolean save(String nombre) throws FileNotFoundException, IOException
    {
        boolean resp=false;
        
        if(dataBases.containsKey(nombre.toUpperCase()))
        {
            
            File db=new File(dir+"/"+nombre.toUpperCase());
            if(!db.exists())
            {   
                if(db.mkdir())
                {
                    FileOutputStream fileOut;
                    fileOut = new FileOutputStream(db.getPath()+"/"+nombre.toUpperCase()+".popo");
                    
                    try (ObjectOutputStream out = new ObjectOutputStream(fileOut)) {
                        out.writeObject(dataBases.get(nombre.toUpperCase()));
                        
                        resp=true;
                    }
                    
                    fileOut.close();

            
                }
                else
                {
                    resp=false;
                }
                
            }
            else
            {
                FileOutputStream fileOut;
                    fileOut = new FileOutputStream(db.getPath()+"/"+nombre.toUpperCase()+".popo");
                    
                    try (ObjectOutputStream out = new ObjectOutputStream(fileOut)) {
                        out.writeObject(dataBases.get(nombre.toUpperCase()));
                        
                        resp=true;
                    }
                    
                    fileOut.close();
                    if(resp)
                    {
                        for ( String key : dataBases.get(nombre.toUpperCase()).getTables().keySet() )
                        {
                            fileOut=new FileOutputStream(db.getPath()+"/"+key+".pipi");
                            try (ObjectOutputStream out = new ObjectOutputStream(fileOut)) 
                            {
                                out.writeObject(dataBases.get(nombre.toUpperCase()).getTable(key));
                                resp=true;
                            }
                            
                            fileOut.close();
                            
                        }   
                    }
            }
            
        }
        else {
            resp=false;
        }
        
        return resp;
        
    }
    
    public boolean createDataBase (String nombre){
        try {
            return create(nombre.toUpperCase());
        } catch (Exception ex) {
            Logger.getLogger(GestorBaseDeDatos.class.getName()).log(Level.SEVERE, null, ex);
            return false;
        }
    }
    
    /**
     * Crea una nueva base de datos
     * @param nombre
     * @return
     * @throws FileNotFoundException
     * @throws IOException 
     */
    public boolean create(String nombre) throws FileNotFoundException, IOException
    {
        
        boolean resp=true;
        
        if(!dataBases.containsKey(nombre.toUpperCase()))
        {
            dataBases.put(nombre.toUpperCase(), new DataBase(nombre.toUpperCase()));
            dataBases.get(nombre.toUpperCase()).setIni(true);
            saveDataBase(nombre.toUpperCase());
            gui.GUI.error("\nBASE DE DATOS "+nombre.toUpperCase()+" CREADA");
        }
        else {
            resp=false;
            gui.GUI.error("Ya existe la base de datos "+nombre);
        }
        
        return resp;
        
    }
    
    /**
     * Elimina una database
     * @param nombre
     * @return true si la elimina, de lo contrario false
     */
    public boolean dropDataBase(String nombre)
    {
        
        boolean resp=true;
        
        if(dataBases.containsKey(nombre.toUpperCase()))
        {
            File directory =new File(dir+"/"+nombre.toUpperCase());
            
            if(directory.exists())
            {
                delete(directory);
                dataBases.remove(nombre.toUpperCase());
                gui.GUI.error("\nBASE DE DATOS "+nombre.toUpperCase()+" ELIMINADA");
            }
            else
            {
                gui.GUI.error("No existe el directorio "+directory.getPath());
            }
            
            
            
        }
        else {
            resp=false;
            gui.GUI.error("No existe esa base de datos");
        }
        
        return resp;
        
    }
    
    /**
     * Revisa si existe una database dentro del gestor
     * @param nombre
     * @return true si existe, si no false
     */
    public boolean checkExists(String nombre)
    {
        boolean resp;
        
        if(dataBases.containsKey(nombre.toUpperCase())) {
            resp=true;
        }
        else {
            resp=false;
        }
        
        return resp;
        
    }

    /**
     * Elimina un directorio
     * @param directory
     * @return true si lo eliminó de lo contrario false
     */
    private boolean delete(File directory) 
    {
        boolean resp=false;
        
        if(directory.isDirectory())
        {
            if(directory.list().length==0)
            {
                directory.delete();
                resp=true;
            }
            else
            {
                String files[]=directory.list();
                for(String temp:files)
                {
                    File borra=new File (directory,temp);
                    delete(borra);
                }
                
                if(directory.list().length==0) {
                    directory.delete();
                }
                
            }
        }
        else {
            directory.delete();
        }
        
        return resp;
        
    }
    
    /**
     * Cambia el nombre de la database
     * @param original
     * @param nuevo
     * @return true si lo cambio, de lo contrario false
     */
    public boolean alterDatabase(String original, String nuevo)
    {
        
        boolean resp=true;
        
        if(dataBases.containsKey(original.toUpperCase()))
        {
            if(dataBases.containsKey(nuevo.toUpperCase())) {
                resp=false;
                gui.GUI.error("Ya existe una database con el nombre "+nuevo);
            }
            else
            {
                
                
                File oldDir=new File(dir+"/"+original.toUpperCase());
                File newDir=new File(dir+"/"+nuevo.toUpperCase());
                if(oldDir.exists())
                {
                    if(oldDir.isDirectory())
                    {
                        oldDir.renameTo(newDir);
                        oldDir=new File(dir+"/"+nuevo.toUpperCase()+"/"+original.toUpperCase()+".popo");
                        newDir=new File(dir+"/"+nuevo.toUpperCase()+"/"+nuevo.toUpperCase()+".popo");
                        if(oldDir.exists())
                        {
                            if(oldDir.isFile())
                            {
                                oldDir.renameTo(newDir);
                                dataBases.get(original).setName(nuevo.toUpperCase());
                                dataBases.put(nuevo.toUpperCase(), dataBases.get(original.toUpperCase()));
                                dataBases.remove(original.toUpperCase());
                                gui.GUI.error("\nLA BASE DE DATOS "+original.toUpperCase()+" HA SIDO RENOMBRADA A "+nuevo.toUpperCase());
                            }
                            else {
                                resp=false;
                                gui.GUI.error("No existe el archivo "+oldDir.getPath());
                            }
                        }
                        else {
                            resp=false;
                            gui.GUI.error("No existe el archivo "+oldDir.getPath());
                        }
                    }
                    else {
                        resp=false;
                        gui.GUI.error("No existe el directorio "+oldDir.getPath());
                    }
                }
                else {
                    resp=false;
                    gui.GUI.error("No existe el directorio "+oldDir.getPath());
                }
                
            }
        }
        else {
            resp=false;
        }
        
        return resp;
        
    }
    /**
     * Muestra todas las bases de datos del gestor
     * @return String con listado de bases de datos
     */
    public String showDatabases()
    {
        
        String resp="Listado de las bases de datos\n";
        
        for (String key: dataBases.keySet())
        {
            resp+=key+"\n";
        }

        return resp;
        
    }
    
    /**
     * Muestra la tabla en la current dataBase
     * @param name
     * @return El string de la tabla indicada
     */
    public String showTable(String name)
    {
        
        String resp="";
        
        if(current!=null) {
            resp="\nTABLA "+name.toUpperCase()+"\n";
            resp+="\n"+current.showTable(name.toUpperCase());
        }
        else
        {
            gui.GUI.error("No se ha elegido una database como current");
        }
        
        return resp;
        
    }
    
    /**
     * Muestra las tablas de la current database
     * @return String con las tablas
     */
    public String showTables()
    {
        
        String resp="";
        
        if(current!=null) {
            resp="TABLAS DE LA BASE DE DATOS "+current.getName();
            resp+=current.showTables();
        }
        else
        {
            gui.GUI.error("No se ha indicado una dataBase como current");
        }
        
        return resp;
        
    }
    
    
    /**
     * Hace el select de las tablas especificadas con las condiciones y en el orden deseado
     * @param columns
     * @param tables
     * @param condicion
     * @param orderBy
     * @return la tabla con los datos del select
     */
    public Table select(ArrayList<String> columns, ArrayList<String> tables, Stack<String>[] condicion, Stack<String>[] orderBy)
    {
        
        Table resp=new Table("select");
        Table extra=new Table("extra");
        ArrayList<Table> usadas=new ArrayList<>();
        boolean error=false;
        int i=0;
        
            
            while ((i < tables.size()) && (!error)) {
                if (!current.checkExists(tables.get(i).toUpperCase())) {
                    error = true;
                    resp = null;
                } else {
                    usadas.add(current.getTable(tables.get(i).toUpperCase()));
                }

                i++;

        }
        if(!error)
        {
            ArrayList<Table> tablas=productoCruz(tables);
            
            //AGREGA TODAS LAS COLUMNAS A LA TABLA
            
            for(int j=0;j<tablas.size();j++)
            {
                for(int k=0;k<tablas.get(j).getCantCol();k++)
                {
                    Column columna=tablas.get(j).getColumns().get(tablas.get(j).getColName(k));
                    if(!extra.checkExists(tablas.get(j).getColName(k)))
                    {
                        extra.setColumn(tablas.get(j).getColName(k), columna);
                        if(columns.get(0).equals("*"))
                        {
                            resp.setColumn(tablas.get(j).getColName(k),columna);
                        }
                        
                    }
                }
            }
            
            
            i=0;
         
            if (!columns.get(0).equals("*")) {
                while ((i < usadas.size()) && (!error)) {


                    int j = 0;
                    while ((j < columns.size()) && (!error)) {
                        if (usadas.get(i).checkExists(columns.get(j).toUpperCase())) {
                            Column columna = tablas.get(i).getColumns().get(columns.get(j).toUpperCase());
                            if (!resp.checkExists(columns.get(j).toUpperCase())) {
                                resp.setColumn(columns.get(j).toUpperCase(), columna);
                            }

                        }
                        j++;
                    }


                    i++;

                }
            }
            if((!columns.get(0).equals("*"))&&(columns.size()!=resp.getCantCol()))
            {
                gui.GUI.error("No todas las columnas existen en las tablas del FROM");
                resp=null;
            }
            else {
                if (condicion != null) {
                    int y=0;
                    for (int j = 0; j < extra.getCantRegistros(); j++) {
                        if (!evalWhere(condicion, extra, j)) {
                            resp.delRegistroNoCheck(y);
                            y--;
                        }
                        y++;
                    }
                }
                
                if(orderBy!=null)
                {
                    resp=sort(resp,orderBy);
                }
            }
                
        }
            
        
        return resp;
        
    }
    
    /**
     * hace merge sort de una tabla segun su order by
     * @param tabla
     * @param orderBy
     * @return la tabla ordenada
     */
    private Table sort(Table tabla, Stack<String>[] orderBy)
    {
        
        Table resp;
        
        if(tabla.getCantRegistros()<=1)
        {
            resp= tabla;
        }
        else
        {
            Table left, right;
            int mid=tabla.getCantRegistros()/2;
            left=tabla.getRegistros(0, mid);
            right=tabla.getRegistros(mid, tabla.getCantRegistros());
            left=sort(left,orderBy);
            right=sort(right,orderBy);
            resp=merge(left, right, orderBy,tabla);
        }
        return resp;
        
    }
    
    /**
     * Une las dos tablas del sort
     * @param left
     * @param right
     * @param orderBy
     * @param original
     * @return la tabla unificada
     */
    private Table merge(Table left, Table right,Stack<String>[] orderBy, Table original)
    {
        
        Table resp=new Table("merge");
        
        for(int i=0;i<original.getPosiciones().size();i++)
        {
            resp.addColumn(original.getColName(i), original.getTipoCol(i));
        }
        
        while(left.getCantRegistros()>0 || right.getCantRegistros()>0)
        {
            if((left.getCantRegistros()>0)&&(right.getCantRegistros()>0))
            {
                if(compara(left,right,orderBy))
                {
                    ArrayList<String> reg=left.getRegistro(0);
                    resp.addNoCheck(reg);
                    left.delRegistroNoCheck(0);
                }
                else
                {
                    resp.addNoCheck(right.getRegistro(0));
                    right.delRegistroNoCheck(0);
                }
                
            }
            else if(left.getCantRegistros()>0)
            {
                resp.addNoCheck(left.getRegistro(0));
                left.delRegistroNoCheck(0);
            }
            else
            {
                resp.addNoCheck(right.getRegistro(0));
                right.delRegistro(0);
            }
        }
        
        return resp;
        
    }
    
    /**
     * Compara dos tablas segun su order by
     * @param left
     * @param right
     * @param orderBy
     * @return true si el valor de la izquierda es mayo al de la derecha
     */
    private boolean compara(Table left, Table right, Stack<String>[] orderBy)
    {
        
        boolean resp=true;
        Stack<String>[] order=new Stack[2];
        order[0]=(Stack)orderBy[0].clone();
        order[1]=(Stack)orderBy[1].clone();
        boolean ya=false;
        
        while((!ya)&&(order[0].size()>0))
        {
            String col=order[0].pop();
            String tipo=order[1].pop();
            String val1=left.getCol(col).getRegistros().get(0);
            String val2=right.getCol(col).getRegistros().get(0);
            int com=comparaTipo(val1,val2,left.getCol(col).getTipo());
            
            if(com!=0)
            {
                if(tipo.equals("ASC"))
                {
                    resp=false;
                }
                else
                {
                    resp=true;
                }
                ya=true;
            }            
        }
        
        if(!ya)
        {
            resp=true;
        }
        
        return resp;
        
    }
    
    /**
     * Compara dos datos de diferente o igual tipo
     * @param val1
     * @param val2
     * @param tipo
     * @return resultado de la comparacion
     */
    private int comparaTipo(String val1, String val2, String tipo)
    {
        
        int resp=0;
        
        if(val1.equals("'NULL'"))
        {
            if(!val2.equals("'NULL'"))
            {
                resp=1;
            }
            else
            {
                resp=0;
            }
        }
        else if(val2.equals("'NULL'"))
        {
            if(val1.equals("'NULL'"))
            {
                resp=0;
            }
            else
            {
                resp=-1;
            }
        }
        else
        {
            switch(tipo)
            {
                case "INT":
                case "FLOAT":
                {
                    float op1=Float.parseFloat(val1);
                    float op2=Float.parseFloat(val2);
                    if(op1<op2)
                    {
                        resp=1;
                    }
                    else if(op1>op2)
                    {
                        resp=-1;
                    } 
                    else
                    {
                        resp=0;
                    }
                    break;
                }
                case "CHAR":
                {
                    if(val1.equals(val2))
                    {
                        resp=0;
                    }
                    else if(val1.compareTo(val2)>0)
                    {
                        resp=1;
                    }
                    else
                    {
                        resp=-1;
                    }
                    break;
                }
                case "DATE":
                {
                    Date op1 = null;
                    Date op2 = null;
                    DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
                    try {
                        op1=df.parse(val1.substring(1, val1.length()-1));
                        op2=df.parse(val2.substring(1,val2.length()-1));
                        } 
                    catch (ParseException ex) {
                        Logger.getLogger(GestorBaseDeDatos.class.getName()).log(Level.SEVERE, null, ex);
                            }
                    
                    if((op1==null)||(op2==null))
                    {
                        resp=0;
                    }
                    else if(op1.compareTo(op2)>0)
                    {
                        resp=1;
                    }
                    else if(op1.compareTo(op2)<0)
                    {
                        resp=-1;
                    }
                    else
                    {
                        resp=0;
                    }
                    break;
                }
            }
        }
        
        return resp;
        
    }
    
    /**
     * Calcula el producto cruz entre una o mas tablas
     * @param tablas
     * @return tablas con el resultado
     */
    private ArrayList<Table> productoCruz(ArrayList<String> tablas)
    {
        
        ArrayList<Table> resp=new ArrayList<>();
        ArrayList<Integer> rep=new ArrayList<>();
        
        for(int i=0;i<tablas.size();i++)
        {
            Table tablaActual= current.getTable(tablas.get(i));
            resp.add(new Table(tablas.get(i)));
            if(i==0)
            {
                rep.add(1);
            }
            else
            {
                rep.add(resp.get(i-1).getCantRegistros());
            }
            
            for(int j=0;j<tablaActual.getCantCol();j++)
            {
                resp.get(i).addColumn(tablaActual.getColName(j), tablaActual.getTipoCol(j));
                ArrayList<String> registros=new ArrayList<>();
                for(int k=0;k<tablaActual.getCantRegistros();k++)
                {
                    for(int z=0;z<(rep.get(i));z++)
                    {
                        registros.add(tablaActual.getCol(j).getRegistros().get(k));
                    }
                }
                
                resp.get(i).getCol(j).setRegistros(registros);
                
            }
            
            for(int j=0;j<i;j++)
            {
                for(int k=0;k<resp.get(j).getCantCol();k++)
                {
                    ArrayList<String> nuevoSet=new ArrayList<>();
                    for(int z=0;z<tablaActual.getCantRegistros();z++)
                    {
                        nuevoSet.addAll(resp.get(j).getCol(k).getRegistros());
                    }
                    resp.get(j).getCol(k).setRegistros(nuevoSet);
                }
            }
            
        }
        
        return resp;
        
    }
    
    /**
     * Obtiene el valor en la columna indicada
     * @param tabla
     * @param numReg
     * @param name
     * @return valor almacenado en la columna
     */
    private String[] getValCampo(Table tabla, int numReg, String name)
    {
        
        String[] resp=new String[2];
        if(tabla.checkExists(name))
        {
            resp[0]=tabla.getCol(name).getRegistros().get(numReg);
            resp[1]=tabla.getTipoCol(name);
        }
        else
        {
            resp=null;
            gui.GUI.error("No existe la columna "+ name+" en la tabla "+tabla.getName());
            //MOSTRAR ERROR!!
        }
        
        return resp;
        
    }
    
    /**
     * Hae la concatenacion de tipos
     * @param tipo1
     * @param tipo2
     * @return Array con los tipos y valores convertidos
     */
    private String[] convTipo(String tipo1, String tipo2)
    {
        
        String[] resp=new String[2];
        
        if(tipo1.equals("CHAR")||(tipo2.equals("CHAR")))
        {
            resp[0]="CHAR";
            resp[1]="CHAR";
        }
        else if(tipo1.equals("FLOAT")||tipo2.equals("FLOAT"))
        {
            if(tipo1.equals("INT")||tipo2.equals("INT"))
            {
                resp[0]="FLOAT";
                resp[1]="FLOAT";
            }
            else
            {
                gui.GUI.error("No puede relacionar valores "+tipo1+" con "+tipo2);
                resp=null;
            }
        }
        else
        {
            resp=null;
        }
        
        return resp;
        
    }
    
    /**
     * Evalua el where para el registro en la tabla indicada
     * @param condicion1
     * @param tabla
     * @param numReg
     * @return el resultado de la evaluacion
     */
    private boolean evalWhere(Stack<String>[] condicion1, Table tabla, int numReg) {
        boolean resp;
        boolean error = false;
        Stack<String>[] condicion = new Stack[2];
        condicion[0]=(Stack<String>) condicion1[0].clone();
        condicion[1]=(Stack<String>) condicion1[1].clone();
        String valTipo;
        Stack<String>[] valores=new Stack[2];
        valores[0] = new Stack<>();
        valores[1] = new Stack<>();

        while ((condicion[0].size() > 0)) {
            valTipo=condicion[1].pop();
            if ((valTipo.equals("OTRO"))||(valTipo.equals("BOOLEAN"))) {
                condicion[1].push(valTipo);
 
                String op1;
                String op2;
                String opera;
                String tipo1;
                String tipo2;

                opera = condicion[0].pop();
                condicion[1].pop();
                op1 = valores[0].pop().toUpperCase();
                tipo1 = valores[1].pop();
                if(opera.equals("NOT"))
                {
                    op2="NOT";
                    tipo2="NOT";
                }
                else
                {
                    op2 = valores[0].pop().toUpperCase();
                    tipo2 = valores[1].pop();
                }
                
                switch (tipo1) {
                    case "COLUMNA":
                        String[] nuevos = getValCampo(tabla, numReg, op1);
                        if (nuevos != null) {
                            op1 = nuevos[0];
                            tipo1 = nuevos[1];
                        } else {
                            error = true;
                        }
                        break;
                    case "DATE":
                        op1=op1.substring(1, op1.length() - 1);
                        break;                    
                }
                
                switch (tipo2) {
                    case "COLUMNA":
                        String[] nuevos = getValCampo(tabla, numReg, op2);
                        if (nuevos != null) {
                            op2 = nuevos[0];
                            tipo2 = nuevos[1];
                        } else {
                            error = true;
                        }
                        break;
                    case "DATE":
                        op2=op2.substring(1, op2.length() - 1);
                        break;
                }

                if (!error) {
                    
                    if((!tipo1.equals(tipo2))&&(!opera.equals("NOT")))
                    {
                        String[] nuevos=convTipo(tipo1,tipo2);
                        if(nuevos==null)
                        {
                            error=true;
                        }
                        else
                        {
                            tipo1=nuevos[0];
                            tipo2=nuevos[1];
                        }
                    }
                }
                
                if((!error)&&((op2.equals("'NULL'")||(op1.equals("'NULL'"))||(op2.equals("NULL")||(op1.equals("NULL"))))))
                {
                    if(op1.equals("NULL"))
                    {
                        op1="'NULL'";
                    }
                    if(op2.equals("NULL"))
                    {
                        op2="'NULL'";
                    }
                    if(comparaNull(op2,op1,opera))
                    {
                        valores[0].push("T");
                        valores[1].push("RES");
                    }
                    else
                    {
                        valores[0].push("F");
                        valores[1].push("RES");
                    }
                }
                
                else if(!error)
                {
                    valores[1].push(tipo2);
                    valores[1].push(tipo1);

                    switch (valores[1].peek()) {

                        case "INT":
                        case "FLOAT": {
                            valores[1].pop();
                            valores[1].pop();
                            float val1 = Float.parseFloat(op2);
                            float val2 = Float.parseFloat(op1);
                            if (compara(val1,val2,opera)) {
                                valores[0].push("T");
                                valores[1].push("RES");
                            } else {
                                valores[0].push("F");
                                valores[1].push("RES");
                            }
                            break;
                        }
                        case "CHAR":
                            valores[1].pop();
                            valores[1].pop();
                            if (compara(op2,op1,opera)) {
                                valores[0].push("T");
                                valores[1].push("RES");
                            } else {
                                valores[0].push("F");
                                valores[1].push("RES");
                            }
                            break;
                        case "RES":
                        {
                            valores[1].pop();
                            valores[1].pop();
                            boolean val1,val2;
                            
                            if(op1.equals("T"))
                            {
                                val2=true;
                            }
                            else
                            {
                                val2=false;
                            }
                            
                            if(op2.equals("T"))
                            {
                                val1=true;
                            }
                            else
                            {
                                val1=false;
                            }
                            
                            if (compara(val1,val2,opera)) {
                                valores[0].push("T");
                                valores[1].push("RES");
                            } else {
                                valores[0].push("F");
                                valores[1].push("RES");
                            }
                            break;
                        }
                        case "DATE": {
                            Date val1 = null;
                            Date val2 = null;
                            valores[1].pop();
                            valores[1].pop();
                            DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
                            try {
                                val1 = df.parse(op2);
                                val2 = df.parse(op1);
                            } catch (ParseException ex) {
                                Logger.getLogger(GestorBaseDeDatos.class.getName()).log(Level.SEVERE, null, ex);
                            }
                            if ((val1 != null) && (val2 != null)) {
                                if (compara(val1,val2,opera)) {
                                    valores[0].push("T");
                                    valores[1].push("RES");
                            }   else {
                                    valores[0].push("F");
                                    valores[1].push("RES");
                                }
                            } else {
                                valores[0].push("F");
                                valores[1].push("RES");
                            }
                            break;
                        }
                    }
                }


            }
            else
            {
                valores[0].push(condicion[0].pop());
                valores[1].push(valTipo);
            }
        }

        if(!error)
        {
            if (valores[0].size() < 1) {
                gui.GUI.error("Error en la condicion");
                resp = false;
            } else {
                if(valores[1].peek().equals("RES"))
                {
                    if(valores[0].peek().equals("T"))
                    {
                        resp=true;
                    }
                    else
                    {
                        resp=false;
                    }
                }
                else
                {
                    gui.GUI.error("Error en las condiciones");
                    resp=false;
                }
            }
        }
        else
        {
            resp=false;
        }
            
        
        return resp;

    }
    
    /**
     * compara dos valores float o int
     * @param val1
     * @param val2
     * @param opera
     * @return el resultado de la comparacion
     */
    private boolean compara(float val1,float val2, String opera)
    {
        
        boolean resp;
        switch(opera)
        {
            case "<":
            {
                if(val1<val2)
                {
                    resp=true;
                }
                else
                {
                    resp=false;
                }
                break;
            }
                
            case ">":
            {
                if(val1>val2)
                {
                    resp=true;
                }
                else
                {
                    resp=false;
                }
                break;
            } 
                
            case "<=":
            {
                if(val1<=val2)
                {
                    resp=true;
                }
                else
                {
                    resp=false;
                }
                break;
            }
                
            case ">=":
            {
                if(val1>=val2)
                {
                    resp=true;
                }
                else
                {
                    resp=false;
                }
                break;
            }
                
            case "=":
            {
                if(val1==val2)
                {
                    resp=true;
                }
                else
                {
                    resp=false;
                }
                break;
            }
                
            case "<>":
            {
                if(val1!=val2)
                {
                    resp=true;
                }
                else
                {
                    resp=false;
                }
                break;
            }
                
            default:
            {
                gui.GUI.error("No puede utilizar los operadores AND OR  y NOT con floats o ints");
                resp=false;
                break;
            }
        }
        
        return resp;
    }
    
    /**
     * Compara dos valores String
     * @param val1
     * @param val2
     * @param opera
     * @return el resultado de la comparacion
     */
    private boolean compara(String val1,String val2, String opera)
    {
        
        boolean resp;
        switch(opera)
        {
            case "<":
            {
                if(val1.compareTo(val2)<0)
                {
                    resp=true;
                }
                else
                {
                    resp=false;
                }
                break;
            }
                
            case ">":
            {
                if(val1.compareTo(val2)>0)
                {
                    resp=true;
                }
                else
                {
                    resp=false;
                }
                break;
            } 
                
            case "<=":
            {
                if(val1.compareTo(val2)<=0)
                {
                    resp=true;
                }
                else
                {
                    resp=false;
                }
                break;
            }
                
            case ">=":
            {
                if(val1.compareTo(val2)>=0)
                {
                    resp=true;
                }
                else
                {
                    resp=false;
                }
                break;
            }
                
            case "=":
            {
                if(val1.compareTo(val2)==0)
                {
                    resp=true;
                }
                else
                {
                    resp=false;
                }
                break;
            }
                
            case "<>":
            {
                if(val1.compareTo(val2)!=0)
                {
                    resp=true;
                }
                else
                {
                    resp=false;
                }
                break;
            }
                
            default:
            {
                gui.GUI.error("No puede usar los operadores AND OR  y NOT con tipos String");
                resp=false;
                break;
            }
        }
        
        return resp;
    }
    
    /**
     * Compara dos valores tipo Date
     * @param val1
     * @param val2
     * @param opera
     * @return el valor de la comparacion
     */
    private boolean compara(Date val1,Date val2, String opera)
    {
        
        boolean resp;
        switch(opera)
        {
            case "<":
            {
                if(val1.compareTo(val2)<0)
                {
                    resp=true;
                }
                else
                {
                    resp=false;
                }
                break;
            }
                
            case ">":
            {
                if(val1.compareTo(val2)>0)
                {
                    resp=true;
                }
                else
                {
                    resp=false;
                }
                break;
            } 
                
            case "<=":
            {
                if(val1.compareTo(val2)<=0)
                {
                    resp=true;
                }
                else
                {
                    resp=false;
                }
                break;
            }
                
            case ">=":
            {
                if(val1.compareTo(val2)>=0)
                {
                    resp=true;
                }
                else
                {
                    resp=false;
                }
                break;
            }
                
            case "=":
            {
                if(val1.compareTo(val2)==0)
                {
                    resp=true;
                }
                else
                {
                    resp=false;
                }
                break;
            }
                
            case "<>":
            {
                if(val1.compareTo(val2)!=0)
                {
                    resp=true;
                }
                else
                {
                    resp=false;
                }
                break;
            }
                
            default:
            {
                gui.GUI.error("No puede utilizar AND OR y NOT con registros Date");
                resp=false;
                break;
            }
        }
        
        return resp;
    }
    
    /**
     * Compara dos valores boolean
     * @param val1
     * @param val2
     * @param opera
     * @return el resultado de la comparacion
     */
    private boolean compara(boolean val1,boolean val2, String opera)
    {
        
        boolean resp;
        switch(opera)
        {
            case "AND":
            {
                if(val1 && val2)
                {
                    resp=true;
                }
                else
                {
                    resp=false;
                }
                break;
            }
                
            case "OR":
            {
                if(val1 || val2)
                {
                    resp=true;
                }
                else
                {
                    resp=false;
                }
                break;
            }
                
            case "NOT":
            {
                if(val1)
                {
                    resp=false;
                }
                else
                {
                    resp=true;
                }
                break;
            }
                
            default:
            {
                gui.GUI.error("Los registros Boolean solo pueden utilizar operadores AND OR y NOT");
                resp=false;
                break;
            }
        }
        
        return resp;
    }
    
    /**
     * Compara dos valores null
     * @param val1
     * @param val2
     * @param opera
     * @return el resultado de la comparacion
     */
    private boolean comparaNull(String val1,String val2, String opera)
    {
        
        boolean resp;
        switch(opera)
        {
            case "=":
            {
                if(val1.equals(val2))
                {
                    resp=true;
                }
                else
                {
                    resp=false;
                }
                break;
            }
               
            case "<>":
            {
                if(val1.equals(val2))
                {
                    resp=false;
                }
                else
                {
                    resp=true;
                }
                break;
            }
                
            default:
            {
                resp=false;
                break;
            }
        }
        
        return resp;
    }
    
    /**
     * Hace update de los campos encontrados en cols con los valores encontrados
     * en values. Revisa si la cantidad de valores a hacer update son menores
     * o iguales a la cantidad de columnas, también revisa que ninguno de los
     * valores sea un key de la tabla.
     * @param values
     * @param cols
     * @return true si pudo hacer el update, false si no
     */
    public String update(String tabla,ArrayList<String> values, ArrayList<String> cols, Stack[] condicion)
    {
        
        String resp="";
        Table tablaActual;
        int cambiados=0;
        
        if(current.checkExists(tabla))
        {
            
            tablaActual=current.getTable(tabla);
            if(cols.size()<=tablaActual.getCantCol())
            {
                boolean error=false;
                int i=0;
                while((!error)&&(i<cols.size()))
                {
                    if(!tablaActual.checkExists(cols.get(i).toUpperCase()))
                    {
                        error=true;
                        resp="La columna "+cols.get(i)+" no existe.";
                    }
                   /* else if(tablaActual.getCol(cols.get(i).toUpperCase()).getKey().getTipo()!=0)
                    {
                        error=true;
                        resp="No puede modificar la columna "+cols.get(i)+" sin hacer drop del key";
                    }*/
                    i++;
                }
                if(!error)
                {
                    if(tablaActual.checkRow(values))
                    {
                        if(!tablaActual.checkConstraints(values))
                        {
                            error=true;
                        }
                        else if(!checkForeign(tablaActual.getName(),values))
                        {
                            error=true;
                        }
                    }
                    else
                    {
                        error=true;
                    }
                }
                if(!error)
                {
                    if(condicion==null)
                    {
                        for(int j=0;j<tablaActual.getCantRegistros();j++)
                        {
                            for(int k=0;k<cols.size();k++)
                            {
                                tablaActual.getCol(k).updateRegistro(j, values.get(k));
                            }
                        }
                        cambiados=tablaActual.getCantRegistros();
                    }
                    else
                    {
                        for(int j=0;j<tablaActual.getCantRegistros();j++)
                        {
                            if(evalWhere(condicion, tablaActual, j))
                            {
                                for(int k=0;k<cols.size();k++)
                                {
                    
                                    tablaActual.getCol(k).updateRegistro(j, values.get(k));
                                    
                                
                                }
                                cambiados++;
                            }
                        }
                    }
                    
                    resp=tablaActual.showTable();
                    resp+="\n CONT("+cambiados+")";
                    saveDataBase(current.getName());
                    
                }    
                
            }
            else
            {
                resp="No hay suficientes columnas en la tabla";
            }
        }
        else
        {
            resp="No existe la tabla "+tabla;
        }
           
        return resp;
        
    }
    
    /**
     * Elimina los registros que cumplan con las condiciones
     * @param name
     * @param condiciones
     * @return la cantidad de registros borrados o el error
     */
    public String deleteRegistro(String name, Stack<String>[]condiciones)
    {
        String resp="";
        int borrados=0;
        
        if(current.checkExists(name.toUpperCase()))
        {
            Table tabla=current.getTable(name);
            int i=0;
            while(i<tabla.getCantRegistros())
            {
                if(evalWhere(condiciones,tabla,i))
                {
                    tabla.delRegistro(i);
                    borrados++;
                    i--;
                }
                i++;
            }
            resp+="\nCONT("+borrados+")";
        }
        else{
            resp="No existe la tabla "+name;
        }
        
        return resp;
    }
    /**
     * Cambia el nombre del archivo especificado
     * @param name
     * @param newName
     * @return true si lo cambio, false de lo contrario
     */
    private boolean renameTableFile(String name, String newName)
    {
        boolean resp=true;
    
        File oldDir=new File(dir+"/"+current.getName()+"/"+name.toUpperCase()+".pipi");
        File newDir=new File(dir+"/"+current.getName()+"/"+newName.toUpperCase()+".pipi");
        if(oldDir.exists())
        {
            oldDir.renameTo(newDir);
        }
        else
        {
            gui.GUI.error("El archivo no existe");
            resp=false;
        }
           
        return resp;
            
    }
    
    /**
     * Cambia el nombre de la tabla
     * @param oldName
     * @param newName
     * @return true si cambió el nombre, de lo contrario false
     */
    public boolean alterTable(String oldName,String newName)
    {
        
        boolean resp;
        
        resp=current.alterTable(oldName, newName);
        if(resp)
        {   
            resp=renameTableFile(oldName,newName);
            saveDataBase(current.getName());
            gui.GUI.error("\nLA TABLA "+oldName.toUpperCase()+" HA SIDO RENOMBRADA A "+newName.toUpperCase());
        }
        
        
        return resp;
        
    }
    
    /**
     * Agrega una constraint con un check
     * @param name
     * @param id
     * @param condicion
     * @return true si la agrega, de lo contrario false
     */
    public boolean addConstraint(String name,String id, Stack<String>[] condicion)
    {
        
        boolean resp=current.getTable(name).addConstraint(id, condicion);
        
        saveDataBase(current.getName());
        
        if(resp)
        {
            gui.GUI.error("\nCONSTRAINT "+name.toUpperCase()+" HA SIDO CREADA");
        }
        
        return resp;
    }
    
    /**
     * Agrega una constraint con un foreign key
     * @param name
     * @param id
     * @param col
     * @param tablaRef
     * @param colRef
     * @return true si se creó, de lo contrario false
     */
    public boolean addConstraint(String name,String id, String col, String tablaRef, String colRef)
    {
        
        boolean resp=true;
        
        if(current.checkExists(name))
        {
            if(current.checkExists(tablaRef))
            {
                if(current.getTable(name).checkExists(col))
                {
                    if(current.getTable(tablaRef).checkExists(colRef))
                    {
                        if(current.getTable(tablaRef).checkPrimary(colRef))
                        {
                            current.getTable(name.toUpperCase()).addConstraint(id.toUpperCase(), col.toUpperCase(), tablaRef.toUpperCase(), colRef.toUpperCase());
                            current.getTable(tablaRef.toUpperCase()).addForeign(colRef.toUpperCase());        
        
                            saveDataBase(current.getName());
                            gui.GUI.error("\nCONSTRAINT "+name.toUpperCase()+" HA SIDO CREADA");
                        }
                        else
                        {
                            resp=false;
                            gui.GUI.error("No puede hacer referencia de llave foranea a una llave que no es primary key");
                            //dropTable(name);
                        }
                    }
                    else
                    {
                        resp=false;
                        gui.GUI.error("La columna "+colRef+" no existe en la tabla "+tablaRef);
                    }
                }
                else
                {
                    resp=false;
                    gui.GUI.error("La columna "+col+" no existe en la tabla "+name);
                }
        }
            else
            {
                resp=false;
                gui.GUI.error("No existe la tabla "+tablaRef);
            }
        }
        else
        {
            resp=false;
            gui.GUI.error("No existe la tabla "+name);
        }
        
        return resp;
        
    }
    
    /**
     * Agrega una constrain a la tabla indicada
     * @param name
     * @param id
     * @param col
     * @return true si pudo ingresarse, de lo contrario false
     */
    public boolean addConstraint(String name,String id, String col)
    {
        
        boolean resp=current.getTable(name.toUpperCase()).addConstraint(id, col);
        
        saveDataBase(current.getName());
        
        if(!resp)
        {
            gui.GUI.error("No se pudo agregar la constraint "+id);
        }
        else
        {
            gui.GUI.error("\nCONSTRAINT "+name.toUpperCase()+" CREADA");
        }
        
        return resp;
        
    }
    
    /**
     * Elimina la constraint de la tabla indicada
     * @param tabla
     * @param cons 
     */
    public void dropConstraint(String tabla, String cons) {

        if (current.checkExists(tabla)) {
            if (current.getTable(tabla).getConstraints().containsKey(cons)) {
                if (current.getTable(tabla).getConstraints().get(cons).getKeys().size() > 0) {
                    ArrayList<Key> keys = current.getTable(tabla).getConstraints().get(cons).getKeys();
                    for (int i = 0; i < keys.size(); i++) {
                        if (keys.get(i).getTipo() == 2) {
                            current.getTable(keys.get(i).getRefDataBase()).lessForeign(keys.get(i).getRefCol());
                        }
                    }
                }
                current.getTable(tabla).dropConstraint(cons);
                saveDataBase(current.getName());
                gui.GUI.error("\nLA CONSTRAINT "+cons.toUpperCase()+" HA SIDO ELIMINADA");
            } else {
                gui.GUI.error("\nNo existe la constraint " + cons);
            }
        } else {
            gui.GUI.error("\nNo existe la tabla " + tabla);
        }

    }
    
    /**
     * Elimina la columna de la tabla indicada
     * @param tabla
     * @param col 
     */
    public void dropColumn(String tabla, String col)
    {
        
        if(current.checkExists(tabla))
        {
            if(current.getTable(tabla).checkExists(col))
            {
                if(current.getTable(tabla).getCol(col).getForeign()<=0)
                {
                    Table tablaActual=current.getTable(tabla);
                    for(String key:tablaActual.getConstraints().keySet())
                    {
                        if(tablaActual.getConstraints().get(key).getKeys().size()>0)
                        {
                            ArrayList<Key> keys=tablaActual.getConstraints().get(key).getKeys();
                            for(int i=0;i<keys.size();i++)
                            {
                                if((keys.get(i).getTipo()==2)&&(keys.get(i).getId().equals(col)))
                                {
                                    current.getTable(keys.get(i).getRefDataBase()).lessForeign(keys.get(i).getRefCol());
                                }
                            }
                        }
                    }
                    
                    current.getTable(tabla).dropColumn(col);
                    gui.GUI.error("\nLA COLUMNA "+col.toUpperCase()+" HA SIDO ELIMINADA");
                }
                else
                {
                    gui.GUI.error("\nNO PUEDE ELIMINAR UNA COLUMNA QUE SEA REFERENCIADA COMO FOREIGN KEY EN OTRAS TABLAS");
                }
                
                
            }
        }
        
    }
    
    /**
     * Revosa si las filas son referenciadas a un foreign key y chequea el valor
     * @param tabla
     * @param row
     * @return true si cumple con la foreign key, de lo contrario false
     */
    public boolean checkForeign(String tabla, ArrayList<String> row) {

        boolean resp = true;

        Table actual = current.getTable(tabla);
        for (String key : actual.getConstraints().keySet()) {
            if (actual.getConstraints().get(key).getKeys().size() > 0) {
                ArrayList<Key> keys = actual.getConstraints().get(key).getKeys();
                for (int i = 0; i < keys.size(); i++) {
                    if (keys.get(i).getTipo() == 2) {
                        String campo = row.get(actual.getColNum(keys.get(i).getId()));
                        if (current.getTable(keys.get(i).getRefDataBase()).getCol(keys.get(i).getRefCol()).findField(campo).isEmpty()) {
                            resp = false;
                        }
                    }
                }
            }
        }
        
        if(!resp)
        {
            gui.GUI.error("No existe el campo en la foreign key");
        }
        
        return resp;
        
    }
    
    /**
     * Inserta las filas en la tabla y columnas especificadas
     * @param tabla
     * @param cols
     * @param row
     * @return true si pudo insertar, de lo contrario false
     */
    public boolean insert(String tabla,ArrayList<String> cols,ArrayList<String> row)
    {
        
        boolean resp;
        
        if(current.checkExists(tabla))
        {
            Table actual=current.getTable(tabla);
            resp=checkForeign(tabla,row);
            if(resp)
            {
                resp=actual.insert(cols, row);
            }
                    
        }
        else
        {
            resp=false;
            gui.GUI.error("No existe la tabla "+tabla);
        }
        
        if(resp)
        {
            gui.GUI.error("\nCOUNT(1)");
        }
        
        return resp;
        
    }
    
    /**
     * Elimina la tabla
     * @param table
     * @return true si se eliminó, false si no
     */
    public boolean dropTable(String table)
    {
        boolean resp;
        
        resp=current.dropTable(table);
        if(resp)
        {
            File file=new File(dir+"/"+current.getName()+"/"+table+".pipi");
            if(file.delete())
            {
                resp=true;
                gui.GUI.error("\nLA TABLA "+table.toUpperCase()+" HA SIDO ELIMINADA");
            }
            else
            {
                resp=false;
                gui.GUI.error("No pudo ser eliminado el archivo "+table+".pipi");
            }
            
            if(resp)
            {
                saveDataBase(current.getName());
            }
            
        }
        else
        {
            gui.GUI.error("No pudo ser eliminada la tabla "+table);
        }
        
        return resp;
    }
    
    
}
